package scuts.core;

import scuts.core.types.LazyIterator;

class FilterArray {
	
	public static function filter <A> (a:Array<A>, filter:A->Bool):Array<A>
	{
		var res = [];
    for ( e in a) {
      if (filter(e)) res.push(e);
    }
    return res;
	}
  public static function filterWithIndex <A> (a:Array<A>, filter:A->Int->Bool):Array<A>
	{
		var res = [];
    for ( i in 0...a.length) {
      var e = a[i];
      if (filter(e, i)) res.push(e);
    }
    return res;
	}
}

class FilterLazyIterator {
  public static function filter < A > (it:LazyIterator<A>, filter:A->Bool):LazyIterator<A>
  {
    return function () return FilterIterator.filter(it(), filter);
  }
}

class FilterIterator {
	
  public static function filter <A> (it:Iterator<A>, filter:A->Bool):Iterator<A>
	{
    // lazy
    var n = null;
		return {
      next : function () {
        var e = n;
        n = null;
        return e;
      },
      hasNext : function () {
        if (n == null) {
          for (i in it) {
            if (filter(i)) {
              n = i;
              break;
            }
          }
        }
        return n != null;
      }
    };
	}
  
	public static function filterToArray <A> (it:Iterator<A>, filter:A->Bool):Array<A>
	{
		var res = [];
		// TODO cast can be solved with type parameter constraints
		return cast doFilter(it, filter, res);
	}
	
	static function doFilter < A > (it:Iterator<A>, filter:A->Bool, cont: { function push (a:A):Dynamic; } ) {
		for (e in it) {
			if (filter(e)) cont.push(e);
		}
		return cont;
	}
	
	public static function filterToList <A> (it:Iterator<A>, filter:A->Bool):List<A>
	{
		var res = new List();
		// TODO cast can be solved with type parameter constraints
		return cast doFilter(it, filter, res); 
	}
}

class FilterIterable {
	public static function filterToArray <A> (it:Iterable<A>, filter:A->Bool)
	{
		return FilterIterator.filterToArray(it.iterator(), filter);
	}
	
	public static function filterToList <A> (it:Iterable<A>, filter:A->Bool)
	{
		return FilterIterator.filterToList(it.iterator(), filter);
	}
}